// 接口：对行为的抽象,接口必须都是抽象的（没有具体的实现） 对比抽象类（有抽象的属性方法，也有具体的实现）
// 接口的概念 就是描述数据的结构、或者形状的， 定义好结构，在去针对结构来进行实现

//1.接口不能有具体的实现， 可以用于描述 函数、 混合类型、对象、类
// type IFullname ={firstname: string;lastname: string;}

interface IFullname1 {
  firstname: string;
  lastname: string;
}
type IFn1 = (obj: IFullname1) => string;
const fullname: IFn1 = ({ firstname, lastname }) => {
  return firstname + lastname;
};
fullname({ firstname: "jiangwen", lastname: "wen" });

// type 和 interface的区别...................................................
//1.如果只是用来描述结构我们采用interface
//2.如果涉及到联合类型，则只能使用type来进行声明
//3.type 不能被扩展， interface 是可以扩展
//4.type 不能重名，interface 重名可以合并
//5.type 在后续的学习中可以使用循环和条件，interface 不行。
//其他情况下无所谓，可以互换 (函数类型一般采用type来声明)

//可以通过接口来声明混合类型..........................................
type IFn = {
  (): number; // 函数a
  count: number; // 函数a,上的属性count
};
// interface IFn {
//   (): number; // 函数a
//   count: number; // 函数a,上的属性count
// }
const click: IFn = () => {
  return click.count++; // 自定义属性
};
//为了防止这个click函数 被重新赋值，let 是可以修改的，如果用const就不一样了
click.count = 0;

//一般情况下:使用接口大概率都是描述对象了
interface IVeg1 {
  // 接口中声明的都是抽象的，而且必须要实现
  readonly color: string; // 仅读属性
  size: number;
  taste?: "sweet" | "sour"; // 可选属性
}

const tomato1: IVeg1 = {
  color: "red",
  size: 20,
  taste: "sour",
};
// tomato1.color = "green"; // 仅读属性,第一次赋值后不能被随意修改
//如何解决多的属性，让他可以赋予给IVeg..........................................................

//1.如果对象中的属性 多于接口可以直接采用断言的方式来赋值
//2.可以基于接口的特性写一个同名的接口
//3.产生新类型，通过继承原有属性的方式
//4.类型兼容
//5.通过任意类型来扩展（常用于一部分格式固定，一部分不固定）
interface IVeg2 {
  readonly color: string;
  size: number;
  taste: string;
  //5.通过任意类型来扩展
  //任意属性，key类型为string时， 可以赋予number，string ，symbol
  // key: string]: any;  any这个位置的值，必须覆盖 readonly color: string;  size: number; taste: string;的数据类型
  //   [key: string]: any; ====>
} // 可选属性

/*
//1.如果对象中的属性 多于接口可以直接采用断言的方式来赋值
const tomato2: IVeg2 = {
  color: "red",
  size: 20,
  taste: "sour",
  a: 1,
} as IVeg2;
*/
/*
//2.可以基于接口的特性写一个同名的接口,同名接口会合并
interface IVeg2 {
  a?: 1;
}
*/
/*
//3.产生新类型，通过继承原有属性的方式
interface IV extends IVeg2 {
  a?: 1;
}
*/
/* 
//4.类型兼容
let obj = {
  color: "red",
  size: 20,
  taste: "sour",
  a: 1,
};
// 将一个值赋予给另一个值，有类型兼容性，可以将子类型赋予给父类型
const tomato2: IVeg2 = obj;
console.log(tomato2); 
*/

interface Person {
  name: string;
  [key: string]: any;
  company: {
    n: 100;
  };
}
let p: Person = {
  name: "jiangwen",
  age: 30,
  company: {
    n: 100,
  },
};
// 数字索引===>可以声明数组和类数组数据类型
interface IArr {
  [key: number]: any;
}
// 类数组
let arr1: IArr = {
  // 限制索引只能是数字
  0: 1,
  1: 2,
  2: 3,
};
// 数组;
let arr2: IArr = [1, 2, 3];
//通过索引访问符，可以取值的类型

// 通过索引访问符，可以取值的类型....................................................................
type PersonNameType = Person["name"]; // 普通的接口属性===> string
type PersonAnyType = Person[string]; //  [key: string]: any; ======> any
type PersonNType = Person["company"]["n"]; // 对象属性  ======> 100
//keyof 取一个对象中key的集合     value0f(自己实现)取值的类型集合
interface ICar {
  color: string;
  a: 1;
  b: 2;
}
// valueof(自己实现)取值的类型集合 // 通过索引操作符获取值的集合
type Valueof = ICar[keyof ICar]; // Valueof====> string | 1 | 2

// 1) 接口可以描述对象结构 （子可以赋予给父）
// interface IPerson {
//     username: string // 类型，不是具体实现
//     age:number
// }
// // 子可以赋予给父亲。 我们需要把一个值赋予给另一个值。 如果是声明的必须必须一致
// let obj = {
//     username:'abc',
//     age:30,
//     address:'地址'
// }
// let person:IPerson = obj; // 赋值的时候 会产生兼容性  （儿子可以赋予给父亲）

// 2） 接口可以描述函数
// interface ICounter {
//     ():number
//     count:number
// }
// // const 标识此值不能修改  let 可以修改的 (如果给函数增加类型定义 函数不能被修改时只能用const)
// const counter:ICounter = () =>{
//     return counter.count++
// }
// counter.count = 0;

// 1）可以通过?表示接口的属性 可有可无
// interface IVeg {
//     name: string
//     taste: string;
//     size: number,
//     color?: string
// }
// 2）断言的方式来进行赋值, 用的最多 as IVeg
// 3）接口的合并 同名的会进行合并,自定义类型的时候 会使用。 自己的业务逻辑用的比较少
// 4) 可以扩展一个新类型 在来使用。 可以扩展属性
// 5）任意类型  随机的属性  描述数字索引的  (除了必有的属性之外 ，其他任意)
// 6）兼容性
interface IVeg {
  readonly name: string; // 只读属性
  taste: string;
  size: number;
}
interface IVegetable extends IVeg {
  color?: "红色";
  [prop: string]: any; // 任意接口 key 随意，值随意
}
const veg: IVegetable = {
  name: "西红柿",
  taste: "甜",
  size: 50,
  color: "红色",
  a: 1,
  b: "2",
  [Symbol()]: "ABC",
  0: 1,
};
interface IArray {
  // 索引接口
  [key: number]: any;
}
// let arr:IArray = [1,2,3]
let arr: IArray = { 0: 1, 1: 2, 2: "abc", 3: true };

interface ResponseData {
  username: string;
  token: string;
}
interface ReturnVal {
  code: number;
  data: ResponseData;
}

// 通过索引访问符 来获取内部类型

type ICode = ReturnVal["code"];
type IUsername = ReturnVal["data"]["username"]; // 可以用于取值的类型
type IKeys = ReturnVal[keyof ReturnVal]; // 取值的类型, 可以采用这种方式

// 接口可以实现， 接口的实现都是通过类来实现 , 类 可以implements实现多个接口...........................................................
// 一个接口可以继承extends多个接口， 接口可以用于继承extends类

interface SpeakChinese {
  speakChinese(): void;
}
interface SpeakEnglish {
  speakEnglish(): void;
}
class Speak {
  public a!: string;
}
interface Speakable extends SpeakEnglish, SpeakChinese, Speak {
  speak(): void; // 实现的是原型方法
  //  speak:()=>void // 实现的是实例方法
}
class Speaker implements Speakable {
  public a!: string;
  speakEnglish(): void {
    throw new Error("Method not implemented.");
  }
  speakChinese(): void {
    throw new Error("Method not implemented.");
  }
  speak() {
    return 100;
  }
}

// 如何表示我要传入的是一个类

class Dog {
  constructor(public name: string) { } // new
}

class Cat {
  constructor(public name: string) { }
}
// 类类型， 不能藐视类本身，描述的是实例
// 类的类型 需要通过typeof 来取类型
// ts的校验规则  鸭子类型检测

// 描述构造函数
// interface IClazz<T>{
//     new (name:string): T
// }
type IClazz<T> = new (name: string) => T;
function createInstance<T>(clazz: IClazz<T>, name: string) {
  return new clazz(name);
}
const instance = createInstance(Cat, "tom");
// 泛型： 泛型坑位 （函数的形式参数） 刚开始类型不确定，通过使用的时候 来确定类型

export { };
